/* Copyright (c) 2010, David Kreuter. See LICENSE for more information */

#include <FL/Fl.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Value_Input.H>
#include <FL/Fl_Progress.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/Fl_File_Chooser.H>
#include <FL/Fl_Check_Button.H>
#include <FL/gl.h>
#include <math.h>

extern "C"{
#include "headers.h"
}

using namespace std;
const double PI=3.141592654;

// The following class only contains code for camera and setup. The code for
// rendering the objects is in the global function draw_scene and gl.c

void draw_scene();
Fl_Check_Button *real_alpha;

class GLView:public Fl_Gl_Window{
public:
	float cx,cy,cz,ud,lr; int mx,my,coords,wire;
	GLView(int X,int Y,int W,int H,const char *title):
		Fl_Gl_Window(X,Y,W,H,title){
		cx=cy=ud=lr=wire=0;cz=10;
		coords=1;
	}
	void vip(){
		double cnear=0.001,cfar=10000.0,fov=0.9,ratio,sqrt_ratio;

		if(h())ratio=(double)w()/(double)h();
		sqrt_ratio=sqrt(ratio);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glViewport(0,0,w(),h());
		glFrustum(-cnear*fov*sqrt_ratio,
		           cnear*fov*sqrt_ratio,
		          -cnear*fov/sqrt_ratio,
		           cnear*fov/sqrt_ratio, cnear, cfar);

		glEnable(GL_DEPTH_TEST);
		glEnable(GL_CULL_FACE);
		glEnable(GL_TEXTURE_2D);
		if(real_alpha->value()){
			glDisable(GL_ALPHA_TEST);
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		}else{
			glDisable(GL_BLEND);
			glEnable(GL_ALPHA_TEST);
			glAlphaFunc(GL_GREATER, 0.0f);
		}
	}
	void draw() {
		if (!valid()){valid(1);vip();}

		glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glRotatef(ud, 1.0f, 0.0f, 0.0f);
		glRotatef(lr, 0.0f, 1.0f, 0.0f);
		glTranslatef(-cx,-cy,-cz);

		if(coords){
			glBindTexture(GL_TEXTURE_2D,0);
			glBegin(GL_LINES);
				glColor3f(1.0f,0.0f,0.0f);
					glVertex3f( 1.0f, 0.0f, 0.0f);
					glVertex3f(-1.0f, 0.0f, 0.0f);
				glColor3f(0.0f,1.0f,0.0f);
					glVertex3f( 0.0f, 1.0f, 0.0f);
					glVertex3f( 0.0f,-1.0f, 0.0f);
				glColor3f(0.0f,0.0f,1.0f);
					glVertex3f( 0.0f, 0.0f, 1.0f);
					glVertex3f( 0.0f, 0.0f,-1.0f);
			glEnd();
		}
		if(wire){
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			glDisable(GL_TEXTURE_2D);
		}
		draw_scene();
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		glEnable(GL_TEXTURE_2D);
	}
	void resize(int X,int Y,int W,int H){
		Fl_Gl_Window::resize(X,Y,W,H);
		vip();redraw();
	}
	int handle(int event){
		switch(event){
		case FL_RELEASE:
		case FL_DRAG: lr-=(mx-Fl::event_x())/2;
		              ud-=(my-Fl::event_y())/2;
		case FL_PUSH: mx=Fl::event_x(); my=Fl::event_y();
		              if(ud>90)ud=90;   if(ud<-90)ud=-90;
		              redraw();
		case FL_FOCUS: case FL_UNFOCUS: return 1;
		case FL_KEYBOARD:{
			float s_lr=sin(lr/180*PI);
			float c_lr=cos(lr/180*PI);
			float s_ud=sin(ud/180*PI);
			float c_ud=cos(ud/180*PI);
			float factor=0.2f;
			switch(Fl::event_key()){
			case 's': factor*=-1; case 'w':
				cx+=factor*s_lr*c_ud;
				cy-=factor*s_ud;
				cz-=factor*c_lr*c_ud;
				redraw();
				return 1;
			case 'd': factor*=-1; case 'a':
				cx-=factor*c_lr;
				cz-=factor*s_lr;
				redraw();
				return 1;
			case 'q': wire=!wire;
				redraw();
				return 1;
			}
		}
		default: return Fl_Gl_Window::handle(event);
		}
	}
};

#define LOCK_START if(apilock)return; apilock=1;
#define LOCK_END   apilock=0;

Fl_Window *win1, *win2;
GLView *gl;
bmd *b;
// Fl_Check_Button *real_alpha; // I copied this line above the class

Fl_Button *load_nswb_button;

static uint8 apilock=0;

void draw_scene(){
	LOCK_START
	if(b) draw_bmd(b);
	//draw_cleanup();
	LOCK_END
}

void redraw_cb(Fl_Widget*, void*){ \
	draw_cleanup();
	gl->redraw();
}

void load_wb(Fl_Widget*, void*){ \
	LOCK_START
	free_bmd(b);
	Fl_File_Chooser chooser(".","*.wb",Fl_File_Chooser::SINGLE,
		"Select your WB");
	chooser.preview(0);
	chooser.show();	while(chooser.shown())Fl::wait();
	uint8 *buf=0; uint32 len;
	if(read_file((char*)chooser.value(),&buf,&len)){
		b=read_bmd(buf+20,*(uint32*)(buf+28));
		free(buf);
	}
	LOCK_END
	gl->redraw();
}

int main() {
	win1=new Fl_Window(400, 400,"3D-View");
		gl=new GLView(0, 0, 400, 400, "");
		win1->resizable(gl);
	win1->end();
	win1->show();
	win2=new Fl_Window(100, 40,"Poke-Tools");
	load_nswb_button=new Fl_Button(0,  0, 100, 20, "Load WB");
	load_nswb_button->callback(load_wb);
	real_alpha=new Fl_Check_Button(0, 20, 100, 20, "Real Alpha");
	real_alpha->callback(redraw_cb);
	win2->end();
	win2->show();
	Fl::run();
	draw_cleanup();
	delete gl,win1,win2;
	delete real_alpha;
	delete load_nswb_button;
	free_bmd(b);
}
